﻿/*
 * Ext Gantt 1.2
 * Copyright(c) 2009-2011 Mats Bryntse Consulting
 * mats@ext-scheduler.com
 * http://www.ext-scheduler.com/license.html
 *
 */

Ext.ns('Sch.gantt');

if (Ext.ux && Ext.ux.grid && Ext.ux.grid.BufferView) {
    /**
     * @class Sch.BufferedGanttView
     * @extends Sch.gantt.GanttView
     * This view class uses functionality from Ext.ux.grid.BufferView and only renders what's visible in the viewport which has a big impact on performance. If you need to show very large datasets in the gantt component, you should probably use this view. 
     * @constructor
     * @param {Object} config The object containing the configuration of this model.
     */
    Sch.gantt.BufferedGanttView = Ext.extend(Sch.gantt.GanttView, {
	    /**
	     * @cfg {Number} rowHeight
	     * The height of a row in the grid.
	     */
	    rowHeight: 22,

	    /**
	     * @cfg {Number} borderHeight
	     * The combined height of border-top and border-bottom of a row.
	     */
	    borderHeight: 0,

	    /**
	     * @cfg {Boolean/Number} scrollDelay
	     * The number of milliseconds before rendering rows out of the visible
	     * viewing area. Defaults to 100. Rows will render immediately with a config
	     * of false.
	     */
	    scrollDelay: Ext.ux.grid.BufferView.prototype.scrollDelay,

	    /**
	     * @cfg {Number} cacheSize
	     * The number of rows to look forward and backwards from the currently viewable
	     * area.  The cache applies only to rows that have been rendered already.
	     */
	    cacheSize: Ext.ux.grid.BufferView.prototype.cacheSize,

	    /**
	     * @cfg {Number} cleanDelay
	     * The number of milliseconds to buffer cleaning of extra rows not in the
	     * cache.
	     */
	    cleanDelay: Ext.ux.grid.BufferView.prototype.cleanDelay,
    
        // private
        isBuffered : true,

        constructor : function() {
		    Sch.gantt.BufferedGanttView.superclass.constructor.apply(this, arguments);
            this.calculatedRowHeight = this.rowHeight + this.borderHeight;
            this.styleRowHeight = Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
            this.on('togglerow', this.update, this);
        },
    
	    initTemplates : function(){
		    Sch.gantt.BufferedGanttView.superclass.initTemplates.call(this);
		    var ts = this.templates;
	    
		    // empty div to act as a place holder for a row
	        ts.rowHolder = new Ext.Template(
		        '<div class="x-grid3-row {alt}" style="{tstyle};{display_style}"></div>'
		    );
		    ts.rowHolder.disableFormats = true;
		    ts.rowHolder.compile();

		    ts.rowBody = new Ext.Template(
		        '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
			    '<tbody><tr>{cells}</tr>',
			    (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
			    '</tbody></table>'
		    );
		    ts.rowBody.disableFormats = true;
		    ts.rowBody.compile();
	    },

	    getVisibleRowCount : function(){
		    var rh = this.calculatedRowHeight,
		        visibleHeight = this.scroller.dom.clientHeight;
		    return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
	    },

	    getVisibleRows: function(){
		    var count = this.getVisibleRowCount(),
                ds = this.ds,
		        sc = this.scroller.dom.scrollTop,
		        startRow = Math.max((sc === 0 ? 0 : Math.floor(sc/this.calculatedRowHeight)-1), 0),
	            nbrVisible = 0,
	            nbrRecords = ds.getCount(),
	            i = 0;
	        
	        // Find start row
	        while (nbrVisible < startRow && i < nbrRecords) {
	            if (ds.isVisibleNode(ds.getAt(i))) {
	                nbrVisible++;
	            }
	            i++;
	        }
	        startRow = i;
	    
	        // Find end row
	        nbrVisible = 0;
	        count += 2; // Render 2 extra rows
	    
	        while (nbrVisible < count && i < nbrRecords) {
	            if (ds.isVisibleNode(ds.getAt(i))) {
	                nbrVisible++;
	            }
	            i++;
	        }
	    
		    return {
			    first: startRow,
			    last: Math.min(i, nbrRecords-1)
		    };
	    },
    
        doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
            var me = this,
                rh = me.styleRowHeight,
		        vr = me.getVisibleRows(),
		        vCount = me.getVisibleRowCount(),
                ts = me.templates, 
                ct = ts.cell, 
                rt = ts.row, 
                last = colCount-1, 
                rb = ts.rowBody,
			    tstyle = 'width:'+me.getTotalWidth()+';height:'+rh+'px;',
			    lstyle = 'width:'+me.getLockedWidth()+';height:'+rh+'px;',
                g = me.grid,
                cm = me.cm,
                processed_cnt = 0,
                timelineRowTpl = me.timelineRowTpl,
			    buf = [], 
                lbuf = [], 
                lcb, 
                c, 
                p = {}, 
                rp = {}, 
                r;
		
            colCount = me.cm.getLockedCount();

		    for(var j = 0, len = rs.length; j < len; j++){
			    r = rs[j]; cb = []; lcb = [];
			    var rowIndex = (j+startRow),
			        visible = (rowIndex >= vr.first && rowIndex <= vr.last),
			        treeVisible = ds.isVisibleNode(r);
			
			    if (visible && treeVisible) {
			        for(var i = 0; i < colCount; i++){
				        c = cs[i];
				        p.id = c.id;
				        p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
                            (cm.config[i].cellCls ? ' ' + cm.config[i].cellCls : '');
				        p.attr = p.cellAttr = '';
				
                        p.value = c.renderer.call(c.scope || c, r.data[c.name], p, r, rowIndex, i, ds);
				        p.style = c.style;
				        if(Ext.isEmpty(p.value)){
					        p.value = '&#160;';
				        }
				        if(me.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
					        p.css += ' x-grid3-dirty-cell';
				        }
				        // ----- Maxim Modification start
                        if (c.id === g.master_column_id) {
                            var depth = ds.getNodeDepth(r);
                            p.treeui = this.renderCellTreeUI(r, ds, depth);
                            p.css += (' sch-gantt-tree-level-' + depth) + (ds.isLeafNode(r) ? ' sch-gantt-leaf-cell' : ' sch-gantt-parent-cell');
                            ct = ts.mastercell;             
                        }
                        else {
                            ct = ts.cell;
                        }
                        // ----- Maxim End of modification
				        lcb[lcb.length] = ct.apply(p);

			        }
                }
			    var alt = [];
			
			    // ----- Maxim Modification start
                if (!treeVisible) {
                    rp.display_style = 'display: none;';
                }
                else {
                    if(stripe && ((processed_cnt+1) % 2 === 0)){
				        alt[0] = 'x-grid3-row-alt';
			        }
                    processed_cnt++;
                    rp.display_style = '';
                }
            
                // ----- Maxim End of modification
            
			    if(r.dirty){
				    alt[1] = ' x-grid3-dirty-row';
			    }
			    rp.cols = colCount;
			    if(me.getRowClass){
				    alt[2] = me.getRowClass(r, rowIndex, rp, ds);
			    }

			    rp.alt = alt.join(' ');
			    rp.tstyle = tstyle;
            
                if (visible && treeVisible) {
                    rp.value = this.timeColumnRenderer(r, ds);
			        buf[buf.length] = onlyBody ? rp.value : timelineRowTpl.apply(rp);
                } else {
			        buf[buf.length] = ts.rowHolder.apply(rp);
                }
			
                rp.cells = lcb.join('');
			    rp.tstyle = lstyle;
                lbuf[lbuf.length] = !(visible && treeVisible) ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
		    }
		    return [buf.join(''), lbuf.join('')];
	    },

	    isRowRendered: Ext.ux.grid.BufferView.prototype.isRowRendered,

	    syncScroll: function(){
            Sch.gantt.BufferedGanttView.superclass.syncScroll.apply(this, arguments);
            var scroll = this.scroller.getScroll().top;

            // prevent sync scroll on horizontal scroll
            if (scroll !== this.lastScrollTop) {
		        this.update();
                this.lastScrollTop = scroll;
            }
	    },

	    update: Ext.ux.grid.BufferView.prototype.update,
    
        onRemove : function(ds, record, index, isUpdate){
            Sch.gantt.BufferedGanttView.superclass.onRemove.apply(this, arguments);
            if(isUpdate !== true){
                this.update();
            }
        },

	    doUpdate: function() {
		    if (this.getVisibleRowCount() > 0) {
			    var me = this,
                    g = me.grid, 
                    cm = g.colModel, 
                    ds = g.getTaskStore(),
	                cs = me.getColumnData(),
	                vr = me.getVisibleRows(),
	                last = vr.last,
	                colCount = cm.getColumnCount(),
	                dpm = me.grid.getDependencyManager(),
                    r,
                    i,
                    visibleRecords = [];
            
			    for (i = vr.first; i <= last; i++) {
                    r = ds.getAt(i);
				    // if row is NOT rendered and is visible, render it
				    if(!me.isRowRendered(i) && ds.isVisibleNode(r)){
					    var html = me.doRender(cs, [r], ds, i, colCount, g.stripeRows, true);
					    me.getRow(i).innerHTML = html[0];
					    me.getLockedRow(i).innerHTML = html[1];
					    visibleRecords.push(r);
				    }
			    }
                dpm.updateDependencies.defer(100, dpm, [visibleRecords]);

			    me.clean();
		    }
	    },

	    // a buffered method to clean rows
	    clean : Ext.ux.grid.BufferView.prototype.clean,

        doClean: function(){
		    if (this.getVisibleRowCount() > 0) {
			    var vr = this.getVisibleRows(),
			        i = 0, 
                    rows = this.getRows(), 
                    lrows = this.getLockedRows();
            
                vr.first -= this.cacheSize;
			    vr.last += this.cacheSize;
			    // if first is less than 0, all rows have been rendered
			    // so lets clean the end...
			    if(vr.first <= 0){
				    i = vr.last + 1;
			    }
			    for(var len = this.ds.getCount(); i < len; i++){
				    // if current row is outside of first and last and
				    // has content, update the innerHTML to nothing
				    if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
					    rows[i].innerHTML = '';
					    lrows[i].innerHTML = '';
				    }
			    }
		    }
	    },

	    layout: function(){
		    Sch.gantt.BufferedGanttView.superclass.layout.call(this);
		    this.update();
	    }
    });
}

